//
// Created by baigui on 21-7-2.
//


/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <cassert>
#include <jni.h>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#include <climits>
#include <semaphore.h>
#include <Looper.h>

// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
//#include <android/log.h>

#define TAG "NativeCodec-looper"

#define DISMISS_LOG 1
#include <log.h>
#undef DISMISS_LOG
//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
//#define LOGV(...) do{}while(0)


struct loopermessage;
typedef struct loopermessage loopermessage;

struct loopermessage {
    long time;
    int what;
    void *obj;
    loopermessage *next;
    bool quit;
};


void *Looper::trampoline(void *p) {
    ((Looper *) p)->loop();
    return NULL;
}

Looper::Looper(std::weak_ptr<CallBack> callBack) {
    LOGV("Looper");
    epollFd = epoll_create(1);
    inotifyFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

    struct epoll_event eventItem;
    eventItem.events = EPOLLIN | EPOLLET;
    eventItem.data.fd = inotifyFd;
    epoll_ctl(epollFd, EPOLL_CTL_ADD, inotifyFd, &eventItem);

    sem_init(&headwriteprotect, 0, 1);
    sem_init(&interpetprotect, 0, 1);
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    pthread_create(&worker, &attr, trampoline, this);
    running = true;
    mCallBack = callBack;
}


Looper::~Looper() {
    if (running) {
        LOGV("Looper deleted while still running. Some messages will not be processed");
        quit();
    }
}

void Looper::post(int what, void *data, uint64_t delay, bool flush) {
    mtime time = mdate() / 1000;
    loopermessage *msg = new loopermessage();
    msg->what = what;
    msg->obj = data;
    msg->next = nullptr;
    msg->time = time + delay;
    msg->quit = false;
    addmsg(msg, flush);
}

void Looper::addmsg(loopermessage *msg, bool flush, bool clear) {
    sem_wait(&headwriteprotect);
    loopermessage *h = head;
    loopermessage *prev = head;
    //移除后面消息
    if (clear) {
        while (h) {
            loopermessage *next = h->next;
            if (next->time >= msg->time) {
                delete h;
                h = next;
            } else {
                continue;
            }
        }
        h = head;
    }
    h = head;
    //特殊情况，主要是删除所有前面消息，
    if (flush) {
        while (h) {
            loopermessage *next = h->next;
            if (next->time <= msg->time) {
                delete h;
                h = next;
            } else {
                h = nullptr;
                break;
            }
        }
    }
    h = head;
    LOGV("-------------- prepare add msg");
    if (h) {
        while (h) {
            if (h->time <= msg->time) {
                LOGV("-------------- prepare add %p  %p",h,h->next);
                prev = h;
                h = h->next;
            } else {
                break;
            }
        }
        if (prev == h){
            head = msg;
            msg->next = prev;
        } else {
            msg->next = h;
            prev->next = msg;
        }
    } else {
        head = msg;
    }
    LOGV("post msg %d", msg->what);
    sem_post(&headwriteprotect);

    if (gettid() == pid) {
        LOGV("add msg sucessv pid: %d",gettid());
        return;
    }
    //
    sem_wait(&interpetprotect);
    need_skip = true;
    sem_post(&interpetprotect);
    //一定唤醒。并且保证循环一次。
    bool exitloop;
    int count = 3;
    do {
        eventfd_write(inotifyFd, 1);
        usleep(5);
        count = count - 1;
        sem_wait(&interpetprotect);
        exitloop = need_skip;
        sem_post(&interpetprotect);
    } while (exitloop && count >= 0);
    LOGV("add msg sucess");
}

void Looper::loop() {
    pid = gettid();
    LOGV("Loop-------start pid:%d ", pid);
    while (true) {
repet:
        LOGV("Loop-----------delay:%lld", delay);
        sem_wait(&interpetprotect);
        LOGV("Loop-----------enter lock");
        if (!need_skip) {
            sem_post(&interpetprotect);
            LOGV("wait---delay:%lld", delay);
            if (delay != 0)
                epoll_wait(epollFd, &pendingEventItems, 4, delay);
            need_skip = false;
        } else{
            need_skip = false;
            LOGV("release lock");
            sem_post(&interpetprotect);
        }
        // get next available message
        sem_wait(&headwriteprotect);
        loopermessage *msg = head;
        if (msg == nullptr) {
            LOGV("no msg");
            delay = -1;
            sem_post(&headwriteprotect);
            continue;
        }

        if (msg->quit) {//理论上是不会有新的消息添加了。
            LOGV("quitting");
            close(epollFd);
            delete msg;
            return;
        }
        delay = msg->time - mdate() / 1000;

        if (delay >= 1) {
            LOGV("nee wait delay:%lld", delay);
            sem_post(&headwriteprotect);
            goto repet;
        }

        //现在消息不为空，并且可以执行，则改变head位置
        head = msg->next;
        delay = 0;
        sem_post(&headwriteprotect);
        LOGV("processing msg %d", msg->what);
        handle(msg->what, msg->obj);
        delete msg;
    }
}

void Looper::quit() {
    LOGV("quit");
    loopermessage *msg = new loopermessage();
    msg->what = 0;
    msg->obj = NULL;
    msg->next = NULL;
    msg->quit = true;
    addmsg(msg, false, true);
    void *retval;
    pthread_join(worker, &retval);
    sem_destroy(&headwriteprotect);
    running = false;
}

void Looper::handle(int what, void *obj) {
    LOGV("dropping msg %d %p", what, obj);
    if (!mCallBack.expired())
        mCallBack.lock()->handle(what, obj);
}